﻿//
//  Copyright (c) 2011 Extreme: Paweł Huryn (lead), Jakub Domański
//

using System;
using System.Collections.Generic;
using System.Linq;
using Extreme.SPFramework.Configuration;
using Extreme.SPFramework.DependencyInjection;
using Microsoft.SharePoint;

namespace Extreme.SPFramework.DomainModel
{
    /// <summary>
    /// Repository base
    /// </summary>
    public abstract class SharepointRepositoryBase<T> : IRepository<T>
        where T : EntityBase, new()
    {
        private Dictionary<string, string> internalColumnNamesByPropertyNames;
        protected readonly EntityMapping mapping;

        private static bool isListValidated = false;
        private static object validationLocker = new object();

        /// <summary>
        /// Creates new instance
        /// </summary>
        public SharepointRepositoryBase()
        {
             mapping = GetMapippingConfiguration().GetEntityMapping<T>();
        }


        /// <summary>
        /// Gets item by unique id
        /// </summary>
        /// <param name="id">Item unique identifier</param>
        /// <returns>Item</returns>
        public virtual T GetItem(Guid id)
        {
            T returnValue = new T();
            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (var webSite = OpenMemoryLeakSafeWebSite())
                {
                    // validates sharepoint list
                    ValidateSharepointListNew();

                    var web = webSite.Web;
                    var list = web.Lists[mapping.SharepointListName];
                    var item = list.GetItemByUniqueId(id);
                    MapToEntity(item, returnValue);
                }
            });
            return returnValue;
        }

        /// <summary>
        /// Gets item by id
        /// </summary>
        /// <param name="id">Item identifier</param>
        /// <returns>Item</returns>
        public virtual T GetItem(int id)
        {
            T returnValue = new T();
            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (var webSite = OpenMemoryLeakSafeWebSite())
                {
                    // validates sharepoint list
                    ValidateSharepointListNew();

                    var web = webSite.Web;
                    var list = web.Lists[mapping.SharepointListName];
                    var item = list.GetItemById(id);
                    MapToEntity(item, returnValue);
                }
            });
            return returnValue;
        }

        /// <summary>
        /// Gets all items
        /// </summary>
        /// <returns>List of all items</returns>
        public virtual List<T> GetAllItems()
        {
            List<T> returnValue = new List<T>();

            var action = new Action(delegate()
            {
                using (var webSite = OpenMemoryLeakSafeWebSite())
                {
                    // validates sharepoint list
                    ValidateSharepointListNew();

                    var web = webSite.Web;
                    var list = web.Lists[mapping.SharepointListName];
                    // Creating a local copy of list item collection. Using list.items[i] in loop couses trafic to db each time You use it.
                    var itemCollection = list.Items;
                    for (int i = 0; i < itemCollection.Count; i++)
                    {
                        var newItem = new T();
                        MapToEntity(itemCollection[i], newItem);
                        returnValue.Add(newItem);
                    }
                }
            });

            SPSecurity.RunWithElevatedPrivileges(action.Invoke);

            return returnValue;
        }

        /// <summary>
        /// Inserts or updates entity, modifies current entity 
        /// (there is no need to get if from database after save to obtain calculated field, dates etc)
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual void SaveItem(T entity)
        {
            if (entity == null)
                throw new ArgumentNullException("entity");

            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (var webSite = OpenMemoryLeakSafeWebSite())
                {
                    // validates sharepoint list
                    ValidateSharepointListNew();

                    var web = webSite.Web;
                    var list = web.Lists[mapping.SharepointListName];

                    if (entity.UniqueId == null)
                    {
                        var newElem = list.Items.Add();
                        MapToListItem(entity, newElem);

                        newElem.Update();
                        UpdateCalculatedFields(list);

                        var savedItem = list.GetItemByUniqueId(newElem.UniqueId);
                        MapToEntity(newElem, entity);
                    }
                    else
                    {
                        var existingItem = list.GetItemByUniqueId(entity.UniqueId.Value);
                        MapToListItem(entity, existingItem);

                        existingItem.Update();
                        UpdateCalculatedFields(list);

                        var savedItem = list.GetItemByUniqueId(entity.UniqueId.Value);
                        MapToEntity(existingItem, entity);
                    }
                }
            });
        }

        /// <summary>
        /// Deletes item
        /// </summary>
        /// <param name="id">Item identifier</param>
        public virtual void DeleteItem(int id)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (var webSite = OpenMemoryLeakSafeWebSite())
                {
                    // validates sharepoint list
                    ValidateSharepointListNew();

                    var web = webSite.Web;
                    var list = web.Lists[mapping.SharepointListName];
                    var item = list.GetItemById(id);
                    if (item != null)
                    {
                        item.Delete();
                    }
                }
            });
        }

        /// <summary>
        /// Deletes item
        /// </summary>
        /// <param name="id">Item idenditier</param>
        public virtual void DeleteItem(Guid id)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (var webSite = OpenMemoryLeakSafeWebSite())
                {
                    var web = webSite.Web;
                    var list = web.Lists[mapping.SharepointListName];
                    var item = list.GetItemByUniqueId(id);
                    if (item != null)
                    {
                        item.Delete();
                    }
                }
            });
        }

        /// <summary>
        /// Gets all items by unique id dictionary
        /// </summary>
        public virtual Dictionary<Guid, T> GetAllItemsByUniqueIdDictionary()
        {
            return GetAllItems().ToDictionary(item => item.UniqueId.Value);
        }

        /// <summary>
        /// Gets all items by d dictionary
        /// </summary>
        public virtual Dictionary<int, T> GetAllItemsByIdDictionary()
        {
            return GetAllItems().ToDictionary(item => item.ListId.Value);
        }

        protected IMappingConfiguration GetMapippingConfiguration()
        {
            var configurations = ServiceProvider.GetAllInstances<IMappingConfiguration>();
            foreach (var config in configurations)
            {
                if (config.IsEntityMappingDefined<T>())
                {
                    return config;
                }
            }
            throw new Exception(String.Format("Entity mapping for type {0} is not defined", typeof(T).FullName));
        }

        /// <summary>
        /// Gets items by SPQuery
        /// </summary>
        /// <param name="queryString">SPQuery inner xml</param>
        /// <returns>List of items</returns>
        protected List<T> GetItemsByQuery(string queryString)
        {
            List<T> returnValue = new List<T>();

            SPQuery query = new SPQuery();
            query.Query = queryString;

            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (var webSite = OpenMemoryLeakSafeWebSite())
                {
                    // validates sharepoint list
                    ValidateSharepointListNew();

                    var web = webSite.Web;
                    var list = web.Lists[mapping.SharepointListName];

                    SPListItemCollection itemCollection = list.GetItems(query);
                    foreach (SPListItem item in itemCollection)
                    {
                        var newItem = new T();
                        MapToEntity(item, newItem);
                        returnValue.Add(newItem);
                    }
                }
            });
            return returnValue;
        }

        /// <summary>
        /// Updates all calculated fields
        /// </summary>
        /// <param name="list"></param>
        protected static void UpdateCalculatedFields(SPList list)
        {
            for (int i = 0; i < list.Fields.Count; i++)
            {
                var field = list.Fields[i];
                if (field is SPFieldCalculated && !field.ReadOnlyField)
                {
                    field.Update();
                }
            }
        }

        /// <summary>
        /// Validates if list for T exist and has all required columns
        /// </summary>
        protected void ValidateSharepointList(SPWeb web)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                SPList list;

                // validate list
                try
                {
                    list = web.Lists[mapping.SharepointListName];
                }
                catch (Exception ex)
                {
                    throw new Exception(String.Format("Error getting list {0}", mapping.SharepointListName), ex);
                }

                // validate columns
                foreach (var key in mapping.PropertiesMapping.Keys)
                {
                    var columnName = mapping.PropertiesMapping[key].ColumnName.Split('.')[0];
                    if (!list.Fields.ContainsField(columnName))
                        throw new Exception(String.Format("List '{0}' doesn't contain field '{1}'", list.Title, columnName));
                }
            });
        }
        /// <summary>
        /// First try to optimize SharepoinTRepositoryBase
        /// Validates if list for T exist and has all required columns
        /// </summary>
        protected void ValidateSharepointListNew()
        {
            // TODO: use ICache instead

            if (isListValidated)
                return;
            
            lock(validationLocker)
            {
                if (isListValidated)
                    return;

                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    using (var webSite = OpenMemoryLeakSafeWebSite())
                    {
                        var web = webSite.Web;
                        SPList list;

                        // validate list
                        try
                        {
                            list = web.Lists[mapping.SharepointListName];
                        }
                        catch (Exception ex)
                        {
                            throw new Exception(String.Format("Error getting list {0}", mapping.SharepointListName), ex);
                        }

                        // validate columns
                        foreach (var key in mapping.PropertiesMapping.Keys)
                        {
                            var columnName = mapping.PropertiesMapping[key].ColumnName.Split('.')[0];
                            if (!list.Fields.ContainsField(columnName))
                                throw new Exception(String.Format("List '{0}' doesn't contain field '{1}'", list.Title,
                                                                  columnName));
                        }
                        isListValidated = true;
                    }
                });

                isListValidated = true;
            }
        }

        /// <summary>
        /// Validates required fields
        /// </summary>
        /// <param name="list">List</param>
        /// <param name="fields">Display names of fields to validate</param>
        protected void ValidateRequiredFields(SPList list, List<string> fields)
        {
            if (list == null)
                throw new ArgumentNullException("list");
            if (fields == null)
                throw new ArgumentNullException("fields");

            foreach (var name in fields)
            {
                if (!list.Fields.ContainsField(name))
                    throw new Exception(String.Format("List '{0}' doesn't contain field '{1}'", list.Title, name));
            }
        }

        /// <summary>
        /// Maps SPListItem to entity of type T
        /// </summary>
        protected virtual void MapToEntity(SPListItem item, T entity)
        {
            entity.ListId = item.ID;
            entity.UniqueId = item.UniqueId;

            // properties dictionary for validation
            var propertiesDic = new Dictionary<string, string>();
            foreach (var prop in typeof(T).GetProperties())
                propertiesDic[prop.Name] = prop.Name;

            // map properties from configuration
            foreach (var propertyName in mapping.PropertiesMapping.Keys)
            {
                if (!propertiesDic.ContainsKey(propertyName))
                    throw new Exception(String.Format("Type '{0}' doesn't have public property with name '{1}'", typeof(T).FullName, propertyName));

                var propertyPath = mapping.PropertiesMapping[propertyName].ColumnName.Split('.');
                var columnName = propertyPath[0];

                object value = item[columnName];
                SPField currentField = null;
                if (item.Fields.ContainsField(columnName))
                    currentField = item.Fields.GetField(columnName);

                if (currentField != null && currentField is SPFieldCalculated)
                {
                    try
                    {
                        value = ((SPFieldCalculated)currentField).GetFieldValueAsText(value);
                        var propertyType = typeof(T).GetProperty(propertyName).PropertyType;
                        value = TryConvertSimpleValue(value, propertyType);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("Error on getting calculated value", ex);
                    }
                }
                else if (value != null)
                {
                    var propertyType = typeof(T).GetProperty(propertyName).PropertyType;
                    value = TryConvertSimpleValue(value, propertyType);
                    if (propertyType == typeof(LookUpItem))
                    {
                        SPFieldLookupValue objLookupFieldValue = new SPFieldLookupValue(value.ToString());

                        LookUpItem tempLookupItem = new LookUpItem(objLookupFieldValue.LookupId, objLookupFieldValue.LookupValue);
                        try
                        {
                            if (currentField != null)
                            {
                                SPFieldLookup lookupFld = (SPFieldLookup)currentField;
                                var sourceList = item.Web.Lists[new Guid(lookupFld.LookupList)];
                                var lookupItem = sourceList.GetItemById(tempLookupItem.LookupId);
                                value = tempLookupItem;
                            }
                            else
                            {
                                value = null;
                            }
                        }
                        catch (ArgumentException)
                        {
                            value = null;
                        }
                    }
                    else if (propertyType == typeof(List<LookUpItem>))
                        value = GetLookupValues(item, columnName);
                    else if (propertyType == typeof(List<string>))
                        value = GetValuesFromMultiChoice(item, columnName);
                    else if (propertyType == typeof(List<PersonInfo>))
                        value = GetPersonCollection(item, columnName);
                    else if (propertyType == typeof(PersonInfo))
                        value = GetPerson(item, columnName);
                }

                try
                {
                    typeof(T).InvokeMember(
                        propertyName,
                        System.Reflection.BindingFlags.SetProperty,
                        null,
                        entity,
                        new object[] { value });
                }
                catch (Exception ex)
                {
                    throw new Exception(String.Format("Error when setting property '{0}' value '{1}'", propertyName, value), ex);
                }
            }
        }

        /// <summary>
        /// Maps entity of type T to SPListItem
        /// </summary>
        protected virtual void MapToListItem(T entity, SPListItem item)
        {
            // base fields
            var allFields = item.ParentList.Fields;
            var editorField = allFields.GetFieldByInternalName("Editor");
            var authorField = allFields.GetFieldByInternalName("Author");
            //var createdField = allFields.GetFieldByInternalName("Created");
            //var modifiedField = allFields.GetFieldByInternalName("Modified");

            // map base properties
            if (SPContext.Current != null)
            {
                if (entity.UniqueId.HasValue)
                {
                    item[editorField.Title] = SPContext.Current.Web.CurrentUser;
                }
                else
                {
                    item[authorField.Title] = SPContext.Current.Web.CurrentUser;
                    item[editorField.Title] = SPContext.Current.Web.CurrentUser;
                }
            }

            // map properties from configuration
            foreach (var propertyName in mapping.PropertiesMapping.Keys)
            {
                if (mapping.PropertiesMapping[propertyName].IsReadOnly)
                    continue;
                var columnName = mapping.PropertiesMapping[propertyName].ColumnName;

                var value = typeof(T).InvokeMember(
                    propertyName,
                    System.Reflection.BindingFlags.GetProperty,
                    null,
                    entity,
                    new object[] { });

                if (value == null)
                {
                    item[columnName] = null;
                    continue;
                }

                var propertyType = typeof(T).GetProperty(propertyName).PropertyType;
                if (propertyType == typeof(double) || propertyType == typeof(double?) ||
                    propertyType == typeof(decimal) || propertyType == typeof(decimal?) ||
                    propertyType == typeof(float) || propertyType == typeof(float?))
                    item[columnName] = Convert.ToDecimal(value);
                else if (propertyType == typeof(TimeSpan) || propertyType == typeof(TimeSpan?))
                {
                    item[columnName] = value.ToString();
                }
                else if (propertyType == typeof(List<LookUpItem>))
                {
                    item[columnName] = FormatValuesForMultipleLookup((List<LookUpItem>)value, item, columnName);
                }
                else if (propertyType == typeof(List<string>))
                {
                    item[columnName] = FormatValuesForMultiChoice((List<string>)value);
                }
                else if (propertyType == typeof(List<PersonInfo>))
                {
                    item[columnName] = FormatUserCollection((List<PersonInfo>)value, item, columnName);

                }
                else if (propertyType == typeof(PersonInfo))
                {
                    var user = (PersonInfo)value;
                    SPUser spuser = item.Web.EnsureUser(user.LoginName);
                    item[columnName] = new SPFieldUserValue(item.Web, spuser.ID, spuser.LoginName);

                }
                else if (propertyType == typeof(LookUpItem))
                {
                    var lookUpItem = (LookUpItem)value;
                    item[columnName] = FormatValuesForLookup(lookUpItem, item, columnName);
                }
                else
                {
                    item[columnName] = value;
                }
            }
        }

        /// <summary>
        /// Gets lookup values
        /// </summary>
        /// <param name="item">SPlist item</param>
        /// <param name="key">Display field name</param>
        protected List<LookUpItem> GetLookupValues(SPListItem item, string key)
        {
            var output = new List<LookUpItem>();
            // SPFieldLookup field = item.Fields[key] as SPFieldLookup;
            SPFieldLookupValueCollection objLookupFieldValueCol = new SPFieldLookupValueCollection(item[key].ToString());
            for (int i = 0; i < objLookupFieldValueCol.Count; i++)
            {
                SPFieldLookupValue singleValue = objLookupFieldValueCol[i];
                output.Add(new LookUpItem(singleValue.LookupId, singleValue.LookupValue));
            }
            return output;
        }

        /// <summary>
        /// Gets values from multi choice
        /// </summary>
        /// <param name="item">SPlist item</param>
        /// <param name="key">Display field name</param>
        /// <returns></returns>
        protected List<string> GetValuesFromMultiChoice(SPListItem item, string key)
        {
            var output = new List<string>();
            SPFieldMultiChoiceValue multiChoiceValue = new SPFieldMultiChoiceValue(item[key].ToString());
            for (int i = 0; i < multiChoiceValue.Count; i++)
            {
                output.Add(multiChoiceValue[i]);
            }
            return output;
        }

        /// <summary>
        /// Gets list of persons
        /// </summary>
        /// <param name="item">List item</param>
        /// <param name="spUserFieldName">Person field display name</param>
        protected List<PersonInfo> GetPersonCollection(SPListItem item, string spUserFieldName)
        {
            List<PersonInfo> users = new List<PersonInfo>();
            SPFieldUserValueCollection fieldValues = item[spUserFieldName] as SPFieldUserValueCollection;
            if (fieldValues != null)
            {
                foreach (SPFieldUserValue value in fieldValues)
                {
                    users.Add(new PersonInfo(value.User.ID, string.Empty,
                        value.User.LoginName, value.User.Name, value.User.Email));
                }


            }
            return users;
        }

        /// <summary>
        /// Gets person
        /// </summary>
        /// <param name="item">List item</param>
        /// <param name="spUserFieldName">Person field display name</param>
        protected PersonInfo GetPerson(SPListItem item, string spUserFieldName)
        {
            SPFieldUser osobaSPFU = (SPFieldUser)item.Fields[spUserFieldName];
            SPFieldUserValue user = (SPFieldUserValue)osobaSPFU.GetFieldValue(item[spUserFieldName].ToString());
            return user != null
                       ? new PersonInfo(user.User.ID, string.Empty, user.User.LoginName, user.User.Name, user.User.Email)
                : null;
        }

        /// <summary>
        /// Formats the list of ids (ids of elements from another list) into SPFieldLookupValueCollection;
        /// </summary>
        /// <param name="input">list od ids of elements from another list</param>
        /// <param name="item">SPListItem</param>
        /// <param name="key"> Column name -  field nam</param>
        protected SPFieldLookupValueCollection FormatValuesForMultipleLookup(List<LookUpItem> input, SPListItem item, string key)
        {
            SPFieldLookup lookupFld = (SPFieldLookup)item.Fields[key];
            SPList sourceList = item.Web.Lists[new Guid(lookupFld.LookupList)];
            var multipleLookUpValues = new SPFieldLookupValueCollection();
            foreach (LookUpItem i in input)
            {
                try
                {
                    SPListItem splookupItem = sourceList.GetItemById(i.LookupId);
                    multipleLookUpValues.Add(new SPFieldLookupValue(splookupItem.ID, splookupItem[lookupFld.LookupField].ToString()));
                }
                catch (ArgumentException ex)
                {
                    throw new ArgumentException(string.Format("Element with id  {0} not found in the {1} list", i.LookupId, item.ParentList.Title), sourceList.Title, ex);
                }
            }
            return multipleLookUpValues;
        }

        protected SPFieldLookupValue FormatValuesForLookup(LookUpItem input, SPListItem item, string key)
        {
            SPFieldLookup lookupFld = (SPFieldLookup)item.Fields[key];
            SPList sourceList = item.Web.Lists[new Guid(lookupFld.LookupList)];
            try
            {
                SPListItem splookupItem = sourceList.GetItemById(input.LookupId);
                return new SPFieldLookupValue(splookupItem.ID, splookupItem[lookupFld.LookupField].ToString());
            }
            catch (ArgumentException ex)
            {
                throw new ArgumentException(string.Format("Element with id  {0} not found in the {1} list", input.LookupId, item.ParentList.Title), sourceList.Title, ex);
            }
        }

        /// <summary>
        /// Formats the value for SpFieldChoice
        /// </summary>
        /// <param name="input">List of values to insert</param>
        /// <returns></returns>
        protected SPFieldMultiChoiceValue FormatValuesForMultiChoice(List<string> input)
        {
            //SPFieldChoice choiceFld = (SPFieldChoice)item.Fields[key];
            var multiChoiceValue = new SPFieldMultiChoiceValue();
            foreach (string s in input)
            {
                multiChoiceValue.Add(s);
            }
            return multiChoiceValue;
        }

        protected SPFieldUserValueCollection FormatUserCollection(List<PersonInfo> usersList, SPListItem item, string key)
        {
            SPFieldUserValueCollection userValueCollection = new SPFieldUserValueCollection();
            foreach (PersonInfo user in usersList)
            {
                SPUser spuser = item.Web.EnsureUser(user.LoginName);
                if (spuser != null)
                {
                    userValueCollection.Add(new SPFieldUserValue(item.Web, spuser.ID, user.LoginName));
                }
                else
                {
                    throw new ArgumentException(string.Format("User {0} not found in the {1} webapplication", user.LoginName, item.Web.Title), "usersList");
                }
            }
            return userValueCollection;
        }

        /// <summary>
        /// Opens new SPWeb (basing on SPWeb.Current or defined mapping)
        /// By default surround it with SPSecurity.RunWithElevatedPrivileges...
        /// </summary>
        [Obsolete("Use 'OpenMemoryLeakSafeWebSite' instead")]
        protected virtual SPWeb OpenWeb()
        {
            SPWeb web;

            if (!String.IsNullOrEmpty(mapping.SiteUrl))
            {
                if (String.IsNullOrEmpty(mapping.WebName))
                {
                    web = new SPSite(mapping.SiteUrl).OpenWeb();
                }
                else
                {
                    web = new SPSite(mapping.SiteUrl).AllWebs[mapping.WebName];
                }
            }
            else
            {
                if (SPContext.Current == null)
                    throw new Exception("SPContext.Current is null. Specify sttribute 'SiteUrl' in entity mapping or run code in sharepoint context");
                web = new SPSite(SPContext.Current.Site.Url).AllWebs[SPContext.Current.Web.ID];
            }

            web.AllowUnsafeUpdates = true;
            return web;
        }

        protected virtual AutoDisposeWebSite OpenMemoryLeakSafeWebSite()
        {
            SPWeb web;
            SPSite site;

            if (!String.IsNullOrEmpty(mapping.SiteUrl))
            {
                if (String.IsNullOrEmpty(mapping.WebName))
                {
                    site = new SPSite(mapping.SiteUrl);
                    web = site.OpenWeb();
                }
                else
                {
                    site = new SPSite(mapping.SiteUrl);
                    web = site.AllWebs[mapping.WebName];
                }
            }
            else
            {
                if (SPContext.Current == null)
                    throw new Exception("SPContext.Current is null. Specify sttribute 'SiteUrl' in entity mapping or run code in sharepoint context");
                site = new SPSite(SPContext.Current.Site.Url);
                web = site.AllWebs[SPContext.Current.Web.ID];
            }

            web.AllowUnsafeUpdates = true;

            return new AutoDisposeWebSite(site, web);
        }

        /// <summary>
        /// Gets Dictionary[Property name, SP internal column name]
        /// </summary>
        protected Dictionary<string, string> GetInternalColumnNamesByPropertyNames()
        {
            if (internalColumnNamesByPropertyNames == null)
            {
                using (var webSite = OpenMemoryLeakSafeWebSite())
                {
                    // validates sharepoint list
                    ValidateSharepointListNew();

                    var web = webSite.Web;
                    var fieldColection = web.Lists[mapping.SharepointListName].Fields;
                    Dictionary<string, string> output = new Dictionary<string, string>();
                    foreach (var key in mapping.PropertiesMapping.Keys)
                    {
                        var colName = mapping.PropertiesMapping[key].ColumnName;
                        output.Add(key, fieldColection[colName].InternalName);
                    }
                    internalColumnNamesByPropertyNames = output;
                }
            }
            return internalColumnNamesByPropertyNames;
        }

        private object TryConvertSimpleValue(object value, Type propertyType)
        {
            if (value == null)
                return null;

            if (propertyType == typeof(double) || propertyType == typeof(double?))
                value = Convert.ToDouble(value);
            else if (propertyType == typeof(int) || propertyType == typeof(int?))
                value = Convert.ToInt32(value);
            else if (propertyType == typeof(decimal) || propertyType == typeof(decimal?))
                value = Convert.ToDecimal(value);
            else if (propertyType == typeof(float) || propertyType == typeof(float?))
                value = float.Parse(value.ToString());
            else if (propertyType == typeof(string))
                value = value.ToString();
            else if (propertyType == typeof(TimeSpan) || propertyType == typeof(TimeSpan?))
                value = TimeSpan.Parse(value.ToString());

            return value;
        }
    }
}
